﻿using System.IO;

namespace MessageHandler
{
	public class Controller
	{
		Connection connection;
		Configuration configuration;

		Serializer serializer;

		UserInfo userInfo;

		public UserInfo UserInfo
		{
			get { return userInfo; }
			set { userInfo = value; }
		}

		public Controller()
		{
			this.serializer = new Serializer(typeof(Message));
		}

		public void InitConfiguration(UserInfo userInfo)
		{
			this.userInfo = userInfo;
			this.configuration = new Configuration();
			configuration.IpAddress = userInfo.ip;
			configuration.Port = userInfo.port;
			this.connection = new Connection(new HandlerAdapter(this));
			this.connection.Connect(configuration);
		}

		#region SendMessages

		public void SendPrivateMessage(string text, params UserInfo[] userInfos)
		{
			Message message = new Message();
			message.text = text;
			message.messageType = MessageType.PRIVATE;
			message.Item = new PrivateMessageType() { from = this.userInfo, to = userInfos };
			SendAsXml(message);
		}

		public void Register(UserInfo userInfo)
		{
			Message message = new Message();
			message.messageType = MessageType.REGISTER;
			message.Item = new RegisterMessageType() { newUser = userInfo };
			SendAsXml(message);
		}

		public void Login(UserInfo newUser)
		{
			Message message = new Message();
			message.messageType = MessageType.USERLOGIN;
			message.text = string.Empty;
			message.Item = new UserLoginMessageType() { newUser = newUser };
			SendAsXml(message);
		}

		public void Logout(UserInfo user)
		{
			Message message = new Message();
			message.messageType = MessageType.USERLOGOUT;
			message.Item = new UserLogoutMessageType() { loggedOutUser = this.userInfo };
			SendAsXml(message);
		}

		public void SendErrorMessage(MessageHandler.UserInfo[] users, string errorText)
		{
			Message message = new Message();
			message.messageType = MessageType.ERROR;
			message.Item = new ErrorMessageType() { from = this.userInfo, to = users };
			message.text = errorText;
			SendAsXml(message);
		}

		public void UserListUpdate()
		{
			Message message = new Message();
			message.messageType = MessageType.USERLISTUPDATE;
			message.Item = new UserListUpdateMessageType() { currentUser = this.userInfo, currentUserList = new UserInfo[0] };
			SendAsXml(message);
		}

		public void SearchForContacts(string searchText)
		{
			Message message = new Message();
			message.messageType = MessageType.CONTACTSEARCH;
			message.Item = new ContactSearchMessageType();
			message.text = searchText;
			SendAsXml(message);
		}

		public void AddContact(UserInfo userInfo)
		{
			Message message = new Message();
			message.messageType = MessageType.CONTACTADD;
			message.Item = new ContactAddMessageType() { fromUser = this.UserInfo, toUser = userInfo };
			SendAsXml(message);
		}

		public void ConfirmContact(UserInfo userInfo)
		{
			Message message = new Message();
			message.messageType = MessageType.CONTACTADD;
			message.Item = new ContactAddMessageType() { toUser = this.UserInfo, fromUser = userInfo };
			SendAsXml(message);
		}

		public void UpdateProfile(UserInfo userInfo)
		{
			Message message = new Message();
			message.messageType = MessageType.PROFILEUPDATE;
			message.Item = new ProfileUpdateMessageType() { userInfo = userInfo };
			SendAsXml(message);
		}

		public void SendStartFileTransfer(string filename, MessageHandler.UserInfo[] users)
		{
			Message message = new Message();
			message.messageType = MessageType.STARTFILETRANSFER;
			message.Item = new StartFileTransferMessageType() { filename = filename, from = this.userInfo, to = users, answer = true };
			SendAsXml(message);
		}

		public void AnswerStartFileTransfer(Message message, bool answer)
		{
			StartFileTransferMessageType type = (StartFileTransferMessageType)message.Item;
			type.answerer = this.userInfo;
			type.answer = answer;
			SendAsXml(message);
		}

		public void SendFile(string fileName, UserInfo recipient, params UserInfo[] userInfos)
		{
			FileInfo fi = new FileInfo(fileName);
			byte[] data = File.ReadAllBytes(fileName);

			Message message = new Message();
			message.messageType = MessageType.FILETRANSFER;
			message.Item = new FileTransferMessageType() { fileName = fi.Name, from = this.userInfo, to = userInfos, someObject = data, recipient = recipient };
			SendAsXml(message);
		}

		private void SendAsXml(Message message)
		{
			MemoryStream stream = new MemoryStream();
			serializer.SerializeObject(message, ref stream);
			connection.Send(stream.ToArray());
		}

		#endregion

		internal void HandleIncomingMessage(byte[] buffer)
		{
			Message message = (Message)serializer.DeserializeObject(new MemoryStream(buffer));
			switch (message.messageType)
			{
				case MessageType.USERLOGIN:
					this.userInfo = ((UserLoginMessageType)message.Item).newUser;
					break;
				case MessageType.USERLOGOUT:
					Close();
					break;
			}
			OnMessageReceived(message);
		}

		internal void HandleError(ChatException err)
		{
			OnErrorOccured(err);
		}

		public void Close()
		{
			try
			{
				Logout(this.userInfo);
				System.Threading.Thread.Sleep(100);
				connection.Close();
			}
			catch (ChatException err)
			{
				OnErrorOccured(err);
			}
		}

		#region EventHandler

		#region MessageReceived

		public delegate void MessageReceivedEventHandler(object sender, MessageReceivedEventArgs e);

		public event MessageReceivedEventHandler MessageReceived;

		protected void OnMessageReceived(Message message)
		{
			if (MessageReceived != null)
			{
				MessageReceivedEventArgs args = new MessageReceivedEventArgs();
				args.Message = message;
				MessageReceived(this, args);
			}
		}

		#endregion

		#region ErrorOccured

		public delegate void ErrorOccuredEventHandler(object sender, ErrorOccuredEventArgs e);

		public event ErrorOccuredEventHandler ErrorOccured;

		protected void OnErrorOccured(ChatException error)
		{
			if (ErrorOccured != null)
			{
				ErrorOccuredEventArgs args = new ErrorOccuredEventArgs();
				args.Error = error;
				ErrorOccured(this, args);
			}
		}
		#endregion

		#endregion
	}
}
